home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OT Virtual Server / OTVirtualServer / OTVirtualServer.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  62.7 KB  |  2,238 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTVirtualServer.c
  3.  
  4.     Contains:    This is an OpenTransport sample server application which demonstrates
  5.                 a fast framework for making an OpenTransport server application.
  6.     
  7.                 This version of the server simply opens a listener endpoint and
  8.                 many endpoints which can accept connections.  When inbound connections
  9.                 are received, it waits to receive a 128 byte "request", then it sends 
  10.                 a predetermined data from memory (not disk) and begins an orderly release 
  11.                 of the connection.
  12.     
  13.                 Future iterations of this program will retreive data from disk to return,
  14.                 demonstrating synchronization methods, and do ADSP, demonstrating 
  15.                 protocol independence.
  16.     
  17.                 You are welcome to use this code in any way to create you own
  18.                 OpenTransport applications.   For more information on this program,
  19.                 please review the document "About OTVirtual Server".
  20.         
  21.                 Go Bears, beat Stanford !!!
  22.         
  23.                 What's new in version 1.0.1:
  24.     
  25.                 (1) Worked around a bug found when using AckSends and sending the same
  26.                 buffer more than once.   See the routine SendData for details.
  27.     
  28.                 To do:
  29.     
  30.                 (1) Improve statistics window.
  31.                 (2) General routine for processing kOTLookErrs
  32.                 (3) Handle inbound T_ORDREL processing inside other notifications.
  33.                 (4) Allow running on OT 1.1 by including a copy of tilisten module to install.
  34.         
  35.  
  36.     Written by: Eric Okholm    
  37.  
  38.     Copyright:    Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
  39.  
  40.                 You may incorporate this Apple sample source code into your program(s) without
  41.                 restriction. This Apple sample source code has been provided "AS IS" and the
  42.                 responsibility for its operation is yours. You are not permitted to redistribute
  43.                 this Apple sample source code as "Apple sample source code" after having made
  44.                 changes. If you're going to re-distribute the source, we require that you make
  45.                 it clear in the source that the code was descended from Apple sample source
  46.                 code, but that you've made changes.
  47.  
  48.     Change History (most recent first):
  49.                 7/22/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  50.                 
  51.  
  52. */
  53.  
  54. #define DoAlert(x)            { sprintf(gProgramErr, x); gProgramState = kProgramError; }        
  55. #define DoAlert1(x, y)        { sprintf(gProgramErr, x, y); gProgramState = kProgramError; }        
  56. #define DoAlert2(x, y, z)    { sprintf(gProgramErr, x, y, z); gProgramState = kProgramError;}    
  57.  
  58. //
  59. //    Program mode
  60. //
  61. //    Before compiling, 
  62. //    set kDebugLevel to 0 for production
  63. //                    or 1 for debug code.
  64. //
  65. //    In production mode, the code attempts to recover cleanly from any problems in encounters.
  66. //    In debug mode, the unexplained phenomenon cause an alert box highlighting the situation
  67. //    to be delivered and then the program exits.
  68. //
  69.  
  70. #define kDebugLevel    1
  71.  
  72. #if kDebugLevel > 0
  73.  
  74. #define DBAlert(x)            DoAlert(x)        
  75. #define DBAlert1(x, y)        DoAlert1(x, y)        
  76. #define DBAlert2(x, y, z)    DoAlert2(x, y, z)    
  77.  
  78. #else
  79.  
  80. #define DBAlert(x)            { }
  81. #define DBAlert1(x, y)        { }
  82. #define DBAlert2(x, y, z)    { }
  83.  
  84. #endif
  85.  
  86. //
  87. //    Include files
  88. //
  89. #include <Dialogs.h>
  90. #include <Events.h>
  91. #include <Fonts.h>
  92. #include <GestaltEqu.h>
  93. #include <Memory.h>
  94. #include <Menus.h>
  95. #include <QuickDraw.h>
  96. #include <SegLoad.h>
  97. #include <stdio.h>
  98. #include <StdLib.h>
  99. #include <String.h>
  100. #include <strings.h>
  101. #include <ToolUtils.h>
  102. #include <Windows.h>
  103.  
  104. #include <OpenTptInternet.h>        // includes OpenTransport.h
  105. #include <OpenTptClient.h>            // needed for OTReleaseBuffer()    
  106.  
  107. //
  108. //    Defines, enums, resource IDs
  109. //
  110. #define kInFront                    (WindowPtr) -1
  111. #define    kWindowResID                128
  112.  
  113.     // Apple Menu
  114. #define kAppleMenuResID                128
  115. #define    kAppleMenuAbout                1
  116.  
  117.     // File Menu
  118. #define kFileMenuResID                129
  119. #define kFileMenuOpen                1
  120. #define    kFileMenuClose                2
  121. #define    kFileMenuQuit                4
  122.  
  123.     // Edit Menu
  124. #define kEditMenuResID                130                // Edit menu is disabled
  125.  
  126.     // Server Menu
  127. #define kServerMenuResID            131
  128. #define kServerMenuTCPPrefs            1
  129.  
  130.     // Alerts, etc.
  131. #define kAlertExitResID                128
  132. #define kAboutBoxResID                130
  133.  
  134.     // TCP Prefs Dialog
  135. #define kTCPPrefsDlogResID            129
  136. #define    kListenerPortDItem            2
  137. #define    kListenerQueueDepthDItem    4
  138. #define    kMaxConnectionsDItem        6
  139. #define    kReturnDataLengthDItem        8
  140. #define kStartStopDItem                9
  141.  
  142.     // Overall program states
  143. enum
  144. {
  145.     kProgramRunning        = 0,
  146.     kProgramDone        = 1,
  147.     kProgramError        = 2
  148. };
  149.  
  150.     // Server states
  151. enum
  152. {
  153.     kServerStopped        = 0,
  154.     kServerRunning        = 1,
  155.     kServerShuttingDown    = 2
  156. };
  157.  
  158.     // Bit numbers in EPInfo stateFlags fields
  159. enum
  160. {
  161.     kBrokenBit                        = 0,
  162.     kOpenInProgressBit                = 1,
  163.     kFlushDisconnectInProgressBit    = 2,
  164.     kIPReuseAddrBit                    = 3,
  165.     kPassconBit                        = 4,
  166.     kGotRequestBit                    = 5,
  167.     kWaitingBit                        = 6
  168. };
  169.  
  170.     // Misc stuff
  171. enum
  172. {
  173.     kDontQueueIt                = 0,
  174.     kQueueIt                    = 1,
  175.     kTimerHitsBeforeAcceptMax    = 2,
  176.     kTimerIntervalInSeconds        = 3,
  177.     kTimerInterval                = (kTimerIntervalInSeconds * 1000),
  178.     kRequestSize                = 128,
  179.     kOTVersion113                = 0x01130000,
  180.     kOTVersion111                = 0x01110000,
  181.     kDataBufSize                = (16 * 1024),
  182.     kTCPKeepAliveInSecs            = (30 * 1000)        // 30 sec in msec.
  183. };
  184.  
  185.  
  186. //
  187. //    Globals
  188. //
  189. int                    gServerState                = kServerStopped;
  190. int                    gProgramState                = kProgramRunning;
  191. char                gProgramErr[128];
  192. DialogPtr            gDialogPtr                    = NULL;
  193. WindowPtr            gWindowPtr                    = NULL;
  194. long                gSleepTicks                    = 60;
  195. Str255                gListenerPortStr            = "\p2001";
  196. long                gListenerPort                = 2001;
  197. Str255                gListenerQueueDepthStr        = "\p20";
  198. long                gListenerQueueDepth            = 20;
  199. Str255                gMaxConnectionsStr            = "\p100";
  200. long                gMaxConnections                = 100;
  201. long                gMaxConnectionsAllowed        = 0;
  202. Str255                gReturnDataLengthStr        = "\p2048";
  203. long                gReturnDataLength            = 2048;
  204. Boolean                gServerRunning                = false;
  205. Str255                gStartStr                    = "\pStart";
  206. Str255                gStopStr                    = "\pStop";
  207. SInt32                gCntrEndpts                    = 0;
  208. SInt32                gCntrIdleEPs                = 0;
  209. SInt32                gCntrBrokenEPs                = 0;
  210. SInt32                gCntrConnections            = 0;
  211. SInt32                gCntrTotalBrokenEPs            = 0;
  212. SInt32                gCntrTotalConnections        = 0;
  213. SInt32                gCntrTotalBytesSent            = 0;
  214. Boolean                gListenPending                = false;
  215. OTLIFO                gIdleEPLIFO;
  216. OTLIFO*                gIdleEPs                    = &gIdleEPLIFO;
  217. OTLIFO                gBrokenEPLIFO;
  218. OTLIFO*                gBrokenEPs                    = &gBrokenEPLIFO;
  219. OTLIFO                gWaitingEPLIFO;
  220. OTLIFO*                gWaitingEPs                    = &gWaitingEPLIFO;
  221. OTConfiguration*    gCfgMaster                    = NULL;
  222. long                gTimerTask                    = 0;
  223. SInt32                gCntrIntervalConnects        = 0;
  224. SInt32                gCntrIntervalBytes            = 0;
  225. SInt32                gConnectsPerSecond            = 0;
  226. SInt32                gConnectsPerSecondMax        = 0;
  227. SInt32                gKBytesPerSecond            = 0;
  228. SInt32                gKBytesPerSecondMax            = 0;
  229. SInt32                gCntrIntervalEventLoop        = 0;
  230. SInt32                gEventsPerSecond            = 1;
  231. SInt32                gEventsPerSecondMax            = 1;
  232. Boolean                gWaitForEventLoop            = false;
  233. Boolean                gDoWindowUpdate                = true;
  234. SInt32                gAllowNewMax                = kTimerHitsBeforeAcceptMax;
  235. OSType                gOTVersionSelector            = 'otvr';
  236. UInt32                gOTVersion;
  237.  
  238. struct EPInfo
  239. {
  240.     EndpointRef        erf;                //    actual endpoint
  241.     OTLink            link;                //    link into an OT LIFO (atomic)
  242.     SInt32            outstandingSends;    //    number of T_MEMORYRELEASED events expected
  243.     unsigned char*    sendPtr;            //    ptr to next byte to send
  244.     UInt32            sendBytes;            //    remaining bytes to send
  245.     SInt32            rcvdBytes;            //    bytes received (we pretend 128 bytes in is a request for download)
  246.     UInt8            stateFlags;            //    various status fields
  247. };
  248. typedef struct EPInfo EPInfo;
  249.  
  250. EPInfo*    gListener    = NULL;
  251. EPInfo* gAcceptors    = NULL;
  252.  
  253. unsigned char gDataBuf[kDataBufSize];
  254.  
  255. //
  256. //    Option structure 
  257. //    
  258. //    This is used to pass down both IP_REUSEADDR and TCP_KEEPALIVE in the
  259. //    same option message
  260. //
  261.  
  262. struct TKeepAliveOpt
  263. {
  264.     UInt32        len;
  265.     OTXTILevel    level;
  266.     OTXTIName    name;
  267.     UInt32        status;
  268.     UInt32        tcpKeepAliveOn;
  269.     UInt32        tcpKeepAliveTimer;
  270. };
  271. typedef struct TKeepAliveOpt TKeepAliveOpt;
  272.  
  273. //
  274. //    OpenTransport Networking Code Prototypes
  275. //
  276. static void            CheckUnbind(EPInfo*, OTResult, Boolean);
  277. static void            DoListenAccept();
  278. static void            DoRcvDisconnect(EPInfo*);
  279. static void            EnterListenAccept();
  280. static Boolean         EPClose(EPInfo*);
  281. static Boolean        EPOpen(EPInfo*,OTConfiguration* cfg);
  282. static void            NetInit(void);
  283. static void            NetShutdown(void);
  284. static pascal void    Notifier(void*, OTEventCode, OTResult, void*);
  285. static void            ReadData(EPInfo*);
  286. static void            Recycle(void);
  287. static void            SendData(EPInfo*);
  288. static void         StartServer(void);
  289. static void         StopServer(void);
  290. static void            TimerInit();
  291. static void            TimerDestroy();
  292. static pascal void    TimerRun(void*);
  293.  
  294. //
  295. //    Macintosh Program Wrapper Prototypes 
  296. //
  297. static void            AboutBox(void);
  298. static void            AlertExit(char* );
  299. static void         MyC2PStr(char*, Str255);
  300. static void            DialogClose(void);
  301. static Boolean         EventDialog(EventRecord*);
  302. static void            EventDrag(WindowPtr, Point);
  303. static void            EventGoAway(WindowPtr, Point);
  304. static void            EventKeyDown(EventRecord*);
  305. static void            EventLoop(void);
  306. static void            EventMouseDown(EventRecord*);
  307. static void         MacInit(void);
  308. static void         MacInitROM(void);
  309. static void         MenuDispatch(long);
  310. static void         MyP2CStr(Str255, char*);
  311. static void         SetupMenus(void);
  312. static void         TCPPrefsDialog(void);
  313. static void            TCPPrefsReset(void);
  314. static void         WindowClose(void);
  315. static void         WindowOpen(void);
  316. static void            WindowUpdate(void);
  317.  
  318.  
  319. //////////////////////////////////////////////////////////////////////////////////////
  320. //
  321. //    OpenTransport Networking Code
  322. //
  323. //    The code in this section provides the networking portions of the 
  324. //    OpenTransport Virtual Server.
  325. //
  326. //////////////////////////////////////////////////////////////////////////////////////
  327.  
  328. //
  329. //    CheckUnbind
  330. //
  331. //    This routine checks the results of an unbind.   Due to various problems
  332. //    in OpenTransport, an OTUnbind can fail for a number of reasons.  This problem
  333. //    is timing related so you usually won't hit it.   When an OTUnbind fails,
  334. //    we assume the best way to recover is to throw the endpoint on the broken
  335. //    list to be recycled.   Later, in the recycle routine, it will be closed
  336. //    and a new endpoint will be opened to replace it.  If the OTUnbind is
  337. //    successful, the endpoint is put back on the free list to be reused.
  338. //
  339. //    Since the unbind failure is timing related, a more efficient solution
  340. //    would probably be to wait and retry the unbind in a few seconds, 
  341. //    expecting that the call would not fail on the next try.
  342. //
  343. static void CheckUnbind(EPInfo* epi, OTResult result, Boolean queueIt)
  344. {
  345.     if (result != kOTNoError)
  346.     {
  347.         if ( OTAtomicSetBit(&epi->stateFlags, kBrokenBit) == 0 )
  348.         {
  349.             //
  350.             //    The OTAtomicSetBit guarantee's that the EPInfo won't be
  351.             //    enqueued twice.   We only enqueue the EPInfo if the previous
  352.             //    state of the bit was 0.
  353.             //
  354.             OTLIFOEnqueue(gBrokenEPs, &epi->link);
  355.             OTAtomicAdd32(1, &gCntrBrokenEPs);
  356.             OTAtomicAdd32(1, &gCntrTotalBrokenEPs);
  357.         }
  358.     }
  359.     else
  360.     {
  361.         if (queueIt)
  362.         {
  363.             OTLIFOEnqueue(gIdleEPs, &epi->link);
  364.             OTAtomicAdd32(1, &gCntrIdleEPs);
  365.             if (gListenPending)
  366.                 EnterListenAccept();
  367.         }
  368.     }
  369. }
  370.  
  371. //
  372. //    EnterListenAccept
  373. //
  374. //    This is a front end to DoListenAccept() which is used whenever 
  375. //    it is not being called from inside the listener endpoint's notifier.
  376. //    We do this for syncrhonization.   If we were processing an OTListen()
  377. //    or an OTAccept() and we were interrupted at the listener endpoint's
  378. //    notifier with a T_LISTEN, etc, it would be inconvenient and would require
  379. //    some more sophisticated synchronization code to deal with the problem.
  380. //    The easy way to avoid this is to do an OTEnterNotifier() on the listener's
  381. //    endpoint.   
  382. //
  383. //    Important note - doing OTEnterNotifier on one endpoint only prevents that
  384. //    endpoint's notifier for interrupting us.   Since the same notifier code
  385. //    is used for lots of endpoints here, remember that the one endpoint's 
  386. //    notifier can interrupt another.   Doing an OTEnterNotifier() on the
  387. //    listener endpoint prevents the listener from interrupting us, but it
  388. //    does not prevent the Notifier() routine from interrupting us via 
  389. //    another endpoint which also uses the same routine.
  390. //
  391. //    Important note #2 - Don't ever do an OTEnterNotifier on an acceptor endpoint
  392. //    before doing the OTAccept().   This confuses OT and creates problems.
  393. //
  394. static void EnterListenAccept()
  395. {
  396.     Boolean    doLeave;
  397.     
  398.     doLeave = OTEnterNotifier(gListener->erf);
  399.     DoListenAccept();
  400.     if (doLeave)
  401.         OTLeaveNotifier(gListener->erf);
  402. }
  403.  
  404. //
  405. //    DoListenAccept
  406. //
  407. //    The handling of a T_LISTEN is greatly simplified by use
  408. //    of the tilisten module, which serializes inbound connections.
  409. //    This means that when doing an OTAccept we won't get a kOTLookErr
  410. //    because another inbound connection arrived and created a T_LISTEN.
  411. //    Without the tilisten module, we have to use the "8 step 
  412. //    listen/accept/disconnect method", which is documented elsewhere.
  413. //    At this point, if we have a free endpoint, accept the connection.
  414. //    If we don't, assume we are overloaded and reject the connection.
  415. //
  416. //    When we are called from inside the notifier due to a T_LISTEN, 
  417. //    DoListenAccept() is called directly.
  418. //
  419. //    When we restart delayed handling of a T_LISTEN, either because of
  420. //    doing a throttle-back or because the program ran out of free endpoints,
  421. //    EnterListenAccept() is called for synchronization on the listener endpoint.
  422. //
  423. static void DoListenAccept()
  424. {
  425.     TCall        call;
  426.     InetAddress    caddr;
  427.     OTResult    lookResult;
  428.     OTLink*        acceptor_link;
  429.     EPInfo*        acceptor;
  430.     OSStatus    err;
  431.     
  432.     //
  433.     //    By deferring handling of a T_LISTEN, we can slow down inbound requests
  434.     //    and get some time to make sure the event loop occurs.   This is important
  435.     //    so that: (1) the user can quit the program, (2) so memory can be restructured,
  436.     //    (3) so we can recycle broken endpoints and other administrative tasks that
  437.     //    are not done in the notifier.
  438.     //
  439.     if (gWaitForEventLoop)
  440.     {
  441.         gListenPending = true;
  442.         return;
  443.     }
  444.     
  445.     //
  446.     //    Get an EPInfo & endpoint.   If none are available, defer handling the T_LISTEN.
  447.     //
  448.     acceptor_link = OTLIFODequeue(gIdleEPs);
  449.     if (acceptor_link == NULL)
  450.     {
  451.         gListenPending = true;
  452.         return;
  453.     }
  454.     
  455.     OTAtomicAdd32(-1, &gCntrIdleEPs);
  456.     gListenPending = false;
  457.     acceptor = OTGetLinkObject(acceptor_link, EPInfo, link);
  458.     acceptor->stateFlags = 0;
  459.     acceptor->rcvdBytes = 0;
  460.         
  461.     call.addr.maxlen = sizeof(InetAddress);
  462.     call.addr.buf = (unsigned char*) &caddr;
  463.     call.opt.maxlen = 0;
  464.     call.opt.buf = NULL;
  465.     call.udata.maxlen = 0;
  466.     call.udata.buf = NULL;
  467.         
  468.     err = OTListen(gListener->erf, &call);
  469.     if (err != kOTNoError)
  470.     {
  471.         //
  472.         //    Only two errors are expected at this point.
  473.         //    One would be a kOTNoDataErr, indicating the inbound connection
  474.         //    was unavailable, temporarily hidden by a higher priority streams
  475.         //    message, etc.   The more likely error is a kOTLookErr, 
  476.         //    which indicates a T_DISCONNECT on the OTLook()
  477.         //    happens when the call we were going to process disconnected.
  478.         //    In that case, go away and wait for the next T_LISTEN event.
  479.         //
  480.         OTLIFOEnqueue(gIdleEPs, &acceptor->link);
  481.         OTAtomicAdd32(1, &gCntrIdleEPs);
  482.         if (err == kOTNoDataErr)
  483.             return;
  484.             
  485.         lookResult = OTLook(gListener->erf);
  486.         if (err == kOTLookErr && lookResult == T_DISCONNECT)
  487.             DoRcvDisconnect(gListener);
  488.         else    
  489.             DBAlert2("Notifier: T_LISTEN - OTListen error %d lookResult %x", err, lookResult);
  490.         return;    
  491.     }
  492.     
  493.     err = OTAccept(gListener->erf, acceptor->erf, &call);
  494.     if (err != kOTNoError)
  495.     {
  496.         //
  497.         //    Again, we have to be able to handle the connection being disconnected
  498.         //    while we were trying to accept it.
  499.         //
  500.         OTLIFOEnqueue(gIdleEPs, &acceptor->link);
  501.         OTAtomicAdd32(1, &gCntrIdleEPs);
  502.         lookResult = OTLook(gListener->erf);
  503.         if (err == kOTLookErr && lookResult == T_DISCONNECT)
  504.             DoRcvDisconnect(gListener);
  505.         else
  506.             DBAlert2("Notifier: T_LISTEN - OTAccept error %d lookResult %x", err, lookResult);
  507.     }
  508. }
  509.  
  510.  
  511. //
  512. //    DoRcvDisconnect
  513. //
  514. //    This routine is called from the notifier in T_LISTEN handling
  515. //    upon getting a kOTLookErr back indicating a T_DISCONNECT needs to be handled.
  516. //
  517. static void DoRcvDisconnect(EPInfo* epi)
  518. {
  519.     OSStatus err;
  520.     
  521.     err = OTRcvDisconnect(epi->erf, NULL);
  522.     if (epi == gListener)
  523.     {
  524.         //
  525.         //    We can get a disconnect on the listener if an inbound connection was
  526.         //    being disconnected (sent a RST) while we were in the process of refusing
  527.         //    it because we had no idle endpoints).   In this case, we don't really
  528.         //    want to do anything other than receive the disconnect and move on.
  529.         //
  530.         if (err != kOTNoError)
  531.             DBAlert1("DoRcvDisconnect: OTRcvDisconnect on listener error %d", err);
  532.         return;
  533.     }
  534.     if (err != kOTNoError)
  535.     {
  536.         if (err != kOTNoDisconnectErr)
  537.             DBAlert1("DoRcvDisconnect: OTRcvDisconnect error %d", err);
  538.         return;
  539.     }
  540.     
  541.     //
  542.     //    Don't start the unbind yet if the endpoint is on the waiting list
  543.     //    and is scheduled for an orderly release (which can no longer happen).
  544.     //    Instead, if it is scheduled, just clear the bit so we know later
  545.     //    to do the unbind instead of the orderly relase.
  546.     //
  547.     if ((OTAtomicClearBit(&epi->stateFlags, kWaitingBit)) == 0)
  548.         CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  549. }
  550.  
  551.  
  552. //
  553. //    DoSndOrderlyDisconnect
  554. //
  555. //    This routine is a front end to OTSndOrderlyDisconnect().
  556. //    In OT 1.1.2 and earlier releases, there is a problem in OT/TCP which can cause
  557. //    OT/TCP to forget to send the orderly release indication upstream if the system
  558. //    is running so fast the event loop doesn't get time.  To work around this problem,
  559. //    we defer sending the orderly release until the event loop runs.   In OT 1.1.3 and
  560. //    later the routine is called from the notifier instead.  The cost of this workaround
  561. //    is about 18% in terms of connections per second, but the workaround appears to
  562. //    be 100% reliable.
  563. //
  564. static void DoSndOrderlyDisconnect(EPInfo* epi)
  565. {
  566.     OSStatus err;
  567.     OTResult epState;
  568.     
  569.     err = OTSndOrderlyDisconnect(epi->erf);
  570.     epState = OTGetEndpointState(epi->erf);
  571.     if (err != kOTNoError)
  572.     {
  573.         DBAlert2("DoSndOrderlyDisconnect: OTSndOrderlyDisconnect error %d state %d", err, epState);
  574.         return;
  575.     }
  576.  
  577.     //
  578.     //    Check the endpoint state to see if we are in T_IDLE.  If so,
  579.     //    the connection is fully broken down and we can unbind are requeue
  580.     //    the endpoint for reuse.   If not, then wait until we have also received
  581.     //    an orderly release from the other side, at which time we will also check 
  582.     //    the state of the endpoint and unbind there if required.
  583.     //
  584.     epState = OTGetEndpointState(epi->erf);
  585.     if (epState == T_IDLE)
  586.     {
  587.         CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  588.     }
  589. }
  590.  
  591. //
  592. //    DoWaitList
  593. //
  594. //    This routine is only used when running on OT 1.1.2 or earlier releases.
  595. //    Check the comments in DoSndOrderlyDisconnect for an explanation.
  596. //    We always check the kWaitingBit to make sure we still need to do the
  597. //    orderly release.   If it has been cleared, then the endpoint has already
  598. //    been disconnected and we can just toss it back into the idle list.
  599. //
  600. static void DoWaitList()
  601. {
  602.     OTLink*     list = OTLIFOStealList(gWaitingEPs);
  603.     OTLink*        link;
  604.     EPInfo*        epi;
  605.  
  606.     while ( (link = list) != NULL )
  607.     {
  608.         list = link->fNext;
  609.         epi = OTGetLinkObject(link, EPInfo, link);
  610.         if ((OTAtomicClearBit(&epi->stateFlags, kWaitingBit)) != 0)
  611.             DoSndOrderlyDisconnect(epi);
  612.         else
  613.             CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  614.     }
  615. }
  616.  
  617.  
  618. //
  619. //    EPClose
  620. //
  621. //    This routine is a front end to OTCloseProvider.   Centralizing closing of
  622. //    endpoints makes debugging and instrumentation easier.   Also, since this
  623. //    program uses Ack Sends to avoid data copies when doing OTSnd(), some special
  624. //    care is required at close time.   
  625. //
  626. static Boolean EPClose(EPInfo* epi)
  627. {
  628.     OSStatus err;
  629.     
  630.     //
  631.     //    If an endpoint is still being opened, we can't close it yet.
  632.     //    There is no way to cancel an OTAsyncOpenEndpoint, so we just
  633.     //    have to wait for the T_OPENCOMPLETE event at the notifier.
  634.     //
  635.     if (OTAtomicTestBit(&epi->stateFlags, kOpenInProgressBit) != 0)
  636.         return false;
  637.     
  638.     //
  639.     //    If the OTAsyncOpenEndpoint failed, the endpoint ref will be NULL,
  640.     //    and we don't need to close it now.
  641.     //
  642.     if (epi->erf == NULL)
  643.         return true;
  644.         
  645.     if (epi->outstandingSends == 0)
  646.     {
  647.         err = OTCloseProvider(epi->erf);
  648.         epi->erf = NULL;
  649.         if (err != kOTNoError)
  650.             DBAlert1("EPClose: OTCloseProvider error %d", err);
  651.         if (epi != gListener)
  652.             gCntrEndpts--;
  653.         return true;
  654.     }
  655.     
  656.     //
  657.     //    If we get to this point, the endpoint did an OTSnd() with AckSends,
  658.     //    and the T_MEMORYRELEASED event hasn't been returned yet.  In order
  659.     //    to make sure we get the event, we flush the stream and then do an
  660.     //    OTDisconnect().   This should get the memory freed so we can close
  661.     //    the endpoint safely.   Note, we set a flag so we don't do this 
  662.     //    more than once on an endpoint.
  663.     //
  664.     if ( OTAtomicSetBit(&epi->stateFlags, kFlushDisconnectInProgressBit) == 0 )
  665.     {
  666.         err = OTIoctl(epi->erf, I_FLUSH, (void *)FLUSHRW);
  667.         if (err != kOTNoError)
  668.             DBAlert1("EPClose: I_FLUSH error %d", err);
  669.     }
  670.     return false;
  671. }
  672.  
  673. //
  674. //    EPOpen:
  675. //
  676. //    A front end to OTAsyncOpenEndpoint.
  677. //    A status bit is set so we know there is an open in progress.
  678. //    It is cleared when the notifier gets a T_OPENCOMPLETE where the context
  679. //    pointer is this EPInfo.  Until that happens, this EPInfo can't be cleaned
  680. //    up and released.
  681. //
  682. static Boolean EPOpen(EPInfo* epi, OTConfiguration* cfg)
  683. {
  684.     OSStatus err;
  685.     
  686.     //
  687.     //    Clear all old state bits and set the open in progress bit.
  688.     //    This doesn't need to be done atomicly because we are 
  689.     //    single threaded on this endpoint at this point.
  690.     //
  691.     epi->erf = NULL;
  692.     epi->stateFlags = 1 << kOpenInProgressBit;        
  693.     err = OTAsyncOpenEndpoint(cfg, 0, NULL, &Notifier, epi);
  694.     if (err != kOTNoError)
  695.     {
  696.         OTAtomicClearBit(&epi->stateFlags, kOpenInProgressBit);
  697.         DBAlert1("EPOpen: OTAsyncOpenEndpoint error %d", err);
  698.         return false;
  699.     }
  700.     return true;
  701. }
  702.  
  703. //
  704. //    NetEventLoop
  705. //
  706. //    This routine is called once during each pass through the program's event loop.
  707. //    If the program is running on OT 1.1.2 or an earlier release, this is where
  708. //    outbound orderly releases are started (see comments in DoSndOrderlyRelease 
  709. //    for more information on that).   This is also where endpoints are "fixed" by
  710. //    closing them and opening a new one to replace them.   This is rarely necessary,
  711. //    but works around some timing issues in OTUnbind().  Having passed through the 
  712. //    event loop once, we assume it is safe to turn off throttle-back.  And, finally,
  713. //    if we have deferred handing of a T_LISTEN, here we start it up again.
  714. //
  715. static void NetEventLoop()
  716. {
  717.     if (gOTVersion < kOTVersion113)
  718.         DoWaitList();
  719.     Recycle();
  720.     gWaitForEventLoop = false;
  721.     if (gListenPending)
  722.         EnterListenAccept();
  723. }
  724.  
  725. //
  726. //    NetInit:
  727. //
  728. //    This routine does various networking related startup tasks:
  729. //
  730. //    (1) it does InitOpenTransport
  731. //    (2) it records the OT version for us.
  732. //    (3) it starts our timer interrupt running.
  733. //
  734. static void NetInit()
  735. {
  736.     OSStatus err;
  737.     
  738.     err = InitOpenTransport();
  739.     if (err)
  740.     {
  741.         DBAlert1("NetInit: InitOpenTransport error %d", err);
  742.         return;
  743.     }
  744.     err = Gestalt(gOTVersionSelector, (long*) &gOTVersion);
  745.     if (err || (gOTVersion < kOTVersion111))
  746.     {
  747.         DoAlert("Please install Open Transport 1.1.1 or later");
  748.         return;
  749.     }
  750.     TimerInit();
  751. }
  752.  
  753. //
  754. //    NetShutdown:
  755. //
  756. //    This routine does various networking related shutdown tasks:
  757. //
  758. static void NetShutdown()
  759. {
  760.     TimerDestroy();
  761.     CloseOpenTransport();
  762. }    
  763.  
  764. //
  765. //    Notifier:
  766. //
  767. //    Most of the interesting networking code in this program resides inside 
  768. //    this notifier.   In order to run asynchronously and as fast as possible,
  769. //    things are done inside the notifier whenever possible.  Since almost
  770. //    everything is done inside the notifier, there was little need for specical
  771. //    synchronization code.
  772. //
  773. //    In the next iteration of this program, when information to be sent is 
  774. //    actually retreived from the disk, the synchronization, particularly for
  775. //    doing sends and handling flow control, will become more complicated. 
  776. //
  777. //    IMPORTANT NOTE:  Normal events defined by XTI (T_LISTEN, T_CONNECT, etc)
  778. //    and OT completion events (T_OPENCOMPLETE, T_BINDCOMPLETE, etc.) are not
  779. //    reentrant.  That is, whenever our notifier is invoked with such an event,
  780. //    the notifier will not be called again by OT for another normal or completion
  781. //    event until we have returned out of the notifier - even if we make OT calls
  782. //    from inside the notifier.   This is a useful synchronization tool.
  783. //    However, there are two kinds of events which will cause the notifier to 
  784. //    be reentered.   One is T_MEMORYRELEASED, which always happens instantly.
  785. //    The other are state change events like kOTProviderWillClose.
  786. //
  787. static pascal void Notifier(void* context, OTEventCode event, OTResult result, void* cookie)
  788. {
  789.     OSStatus err;
  790.     OTResult epState;
  791.     EPInfo* epi = (EPInfo*) context;
  792.  
  793.     //
  794.     //    Once the program is shutting down, most events would be uninteresting.
  795.     //    However, we still need T_OPENCOMPLETE and T_MEMORYRELEASED events since
  796.     //    we can't call CloseOpenTransport until all OTAsyncOpenEndpoints and
  797.     //    OTSends with AckSends have completed.   So those specific events
  798.     //    are still accepted.
  799.     //
  800.     if (gProgramState != kProgramRunning)
  801.     {
  802.         if ((event != T_OPENCOMPLETE) && (event != T_MEMORYRELEASED))
  803.         {
  804.             return;
  805.         }
  806.     }
  807.  
  808.     //
  809.     //    This really isn't necessary, it's just a sanity check which should be removed
  810.     //    once a program is debugged.   It's just making sure we don't get event notifications
  811.     //    after all of our endpoints have been closed.
  812.     //
  813.     if (gServerState == kServerStopped)
  814.     {
  815.         DBAlert1("Notifier: got event %d when server not running!", event);
  816.         return;
  817.     }
  818.     
  819.     //
  820.     //    Within the notifier, all action is based on the event code.
  821.     //    In this notifier, fatal errors all break out of the switch to the bottom.
  822.     //    As long as everything goes as expected, the case returns rather than breaks.
  823.     //
  824.     switch (event)
  825.     {
  826.         //
  827.         //    kStreamIoctlEvent:
  828.         //
  829.         //    This event is returned when an I_FLUSH ioctl has completed.
  830.         //    The flush was done in an attempt to get back all T_MEMORYRELEASED events
  831.         //    for outstanding OTSnd() calls with Ack Sends.   For good measure, we
  832.         //    send a disconnect now.   Errors are ignored at this point since it is
  833.         //    possible that the connection will already be gone, etc.
  834.         //
  835.         case kStreamIoctlEvent:
  836.         {
  837.             if (OTAtomicTestBit(&epi->stateFlags, kOpenInProgressBit) != 0)
  838.                 (void) OTSndDisconnect(epi->erf, NULL);
  839.             return;
  840.         }
  841.         
  842.         //
  843.         //    T_ACCEPTCOMPLETE:
  844.         //
  845.         //    This event is received by the listener endpoint only.   
  846.         //    The acceptor endpoint will get a T_PASSCON event instead.
  847.         //
  848.         case T_ACCEPTCOMPLETE:
  849.         {
  850.             if (result != kOTNoError)
  851.                 DBAlert1("Notifier: T_ACCEPTCOMPLETE - result %d", result);
  852.             return;
  853.         }
  854.         
  855.         //
  856.         //    T_BINDCOMPLETE:
  857.         //
  858.         //    We only bind the listener endpoint, and bind failure is a fatal error.  
  859.         //    Acceptor endpoints are bound within the OTAccept() call when they get a connection.
  860.         //
  861.         case T_BINDCOMPLETE:
  862.         {
  863.             if (result != kOTNoError)
  864.                 DoAlert("Unable to set up listening endpoint, exiting");
  865.             return;
  866.         }
  867.         
  868.         //
  869.         //    T_DATA:
  870.         //
  871.         //    The main rule for processing T_DATA's is to remember that once you have
  872.         //    a T_DATA, you won't get another one until you have read to a kOTNoDataErr.
  873.         //    The advanced rule is to remember that you could get another T_DATA
  874.         //    during an OTRcv() which will eventually return kOTNoDataErr, presenting
  875.         //    the application with a synchronization issue to be most careful about.
  876.         //    
  877.         //    In this application, since an OTRcv() calls are made from inside the notifier,
  878.         //    this particular synchronization issue doesn't become a problem.
  879.         //
  880.         case T_DATA:
  881.         {
  882.             //
  883.             //    Here we work around a small OpenTransport bug.
  884.             //    It turns out, since this program does almost everything from inside the notifier,
  885.             //    that during a T_UNBINDCOMPLETE we can put an EPInfo back into the idle list.
  886.             //    If that notification is interrupted by a T_LISTEN at the notifier, we could
  887.             //    end up starting a new connection on the endpoint before OT unwinds the stack
  888.             //    out of the code which delivered the T_UNBINDCOMPLETE.   OT has some specific
  889.             //    code to protect against a T_DATA arriving before the T_PASSCON, but in this
  890.             //    case it gets confused and the events arrive out of order.   If we try to
  891.             //    do an OTRcv() at this point we will get a kOTStateChangeErr because the endpoint
  892.             //    is still locked by the earlier OTAccept call until the T_PASSCON is delivered
  893.             //    to us.   This is fairly benign and can be worked around easily.  What we do
  894.             //    is note that the T_PASSCON hasn't arrived yet and defer the call to ReadData()
  895.             //    until it does.
  896.             //
  897.             if ( OTAtomicSetBit(&epi->stateFlags, kPassconBit) != 0 )
  898.             {
  899.                 //
  900.                 //    Because are are running completely inside notifiers,
  901.                 //    it is possible for a T_DATA to beat a T_PASSCON to us.
  902.                 //    We need to help OT out when this occurs and defer the
  903.                 //    data read until the T_PASSCON arrives.
  904.                 //
  905.                 ReadData(epi);
  906.             }
  907.             return;
  908.         }
  909.         
  910.         //
  911.         //    T_DISCONNECT:
  912.         //
  913.         //    An inbound T_DISCONNECT event usually indicates that the other side of the
  914.         //    connection did an abortive disconnect (as opposed to an orderly release).
  915.         //    It also can be generated by the transport provider on the system (e.g. tcp)
  916.         //    when it decides that a connection is no longer in existance.
  917.         //
  918.         //    We receive the disconnect, but this program ignores the associated reason (NULL param).
  919.         //    It is possible to get back a kOTNoDisconnectErr from the OTRcvDisconnect call.
  920.         //    This can happen when either (1) the disconnect on the stream is hidden by a 
  921.         //    higher priority message, or (2) something has flushed or reset the disconnect
  922.         //    event in the meantime.   This is not fatal, and the appropriate thing to do is
  923.         //    to pretend the T_DISCONNECT event never happened.   Any other error is unexpected
  924.         //    and needs to be reported so we can fix it.  Next, unbind the endpoint so we can
  925.         //    reuse it for a new inbound connection.
  926.         //    
  927.         //    It is possible to get an error on the unbind due to a bug in OT 1.1.1 and earlier.
  928.         //    The best thing to do for that is close the endpoint and open a new one to replace it.
  929.         //    We do this back in the main thread so we don't have to deal with synchronization problems.
  930.         //
  931.         case T_DISCONNECT:
  932.         {
  933.             DoRcvDisconnect(epi);
  934.             return;
  935.         }
  936.         
  937.         //
  938.         //    T_DISCONNECTCOMPLETE:
  939.         //
  940.         //    Sometimes this is called as a result of the 
  941.         //    I_FLUSH / OTSndDisconenct() combo in StopServer to relaim
  942.         //    all memory via T_MEMORYRELEASED events so we can close down.
  943.         //    We don't actually release any memory or remove the EPInfo
  944.         //    from a list so we don't have to synchronize with the main
  945.         //    thread.   It will get cleaned up on the next call to StopServer().
  946.         //
  947.         //    Note, this is where we would normally clear the stateFlags
  948.         //    for kFlushDisconnectInProgress, but since there is no point in
  949.         //    doing the flush/disconnect more than once, we never clear it.
  950.         //
  951.         //
  952.         case T_DISCONNECTCOMPLETE:
  953.         {
  954.             if (result != kOTNoError)
  955.                 DBAlert1("Notifier: T_DISCONNECT_COMPLETE result %d", result);
  956.             return;
  957.         }
  958.         
  959.         //
  960.         //    T_GODATA:
  961.         //
  962.         //    This event is received when flow control is lifted.   We are under flow control
  963.         //    whenever OTSnd() returns a kOTFlowErr or accepted less bytes than we attempted
  964.         //    to send.  Since SendData() is only called from inside the notifier, we don't
  965.         //    have to worry about interrupting another call to SendData() at this point.
  966.         //
  967.         //    Note, it is also possible to get a T_GODATA without having invoke flow control.
  968.         //    Be safe and prepare for this.
  969.         //
  970.         case T_GODATA:
  971.         {
  972.             SendData(epi);
  973.             return;
  974.         }
  975.         
  976.         //
  977.         //    T_LISTEN:
  978.         //
  979.         //    Call DoListenAccept() to do all the work.
  980.         //
  981.         case T_LISTEN:
  982.         {
  983.             DoListenAccept();
  984.             return;
  985.         }
  986.  
  987.         //
  988.         //    T_OPENCOMPLETE:
  989.         //
  990.         //    This event occurs when an OTAsyncOpenEndpoint() completes.   Note that this event,
  991.         //    just like any other async call made from outside the notifier, can occur during
  992.         //    the call to OTAsyncOpenEndpoint().  That is, in the main thread the program did
  993.         //    the OTAsyncOpenEndpoint(), and the notifier is invoked before control is returned
  994.         //    to the line of code following the call to OTAsyncOpenEndpoint().   This is one
  995.         //    event we need to keep track of even if we are shutting down the program since there
  996.         //    is no way to cancel outstanding OTAsyncOpenEndpoint() calls.
  997.         //
  998.         case T_OPENCOMPLETE:
  999.         {
  1000.             TOptMgmt         optReq;
  1001.             TOption            opt;
  1002.                 
  1003.             OTAtomicClearBit(&epi->stateFlags, kOpenInProgressBit);
  1004.             if (result == kOTNoError)
  1005.                 epi->erf = (EndpointRef) cookie;
  1006.             else
  1007.             {
  1008.                 DBAlert1("Notifier: T_OPENCOMPLETE result %d", result);
  1009.                 return;
  1010.             }
  1011.  
  1012.             if (gProgramState != kProgramRunning)
  1013.                 return;
  1014.             
  1015.             if (epi != gListener)
  1016.                 gCntrEndpts++;
  1017.             
  1018.             //
  1019.             //    Set to blocking mode so we don't have to deal with kEAGAIN errors.
  1020.             //    Async/blocking is the best mode to write an OpenTransport application in (imho).
  1021.             //
  1022.             err = OTSetBlocking(epi->erf);
  1023.             if (err != kOTNoError)
  1024.             {
  1025.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTSetBlocking error %d", err);
  1026.                 return;
  1027.             }
  1028.             
  1029.             //
  1030.             //    Set to AckSends so OT doesn't slow down to copy data sent out.
  1031.             //    However, this requires special care when closing endpoints, so don't use
  1032.             //    AckSends unless you are prepared for this.   Never, ever, close an endpoint
  1033.             //    when a send has been done but the T_MEMORYRELEASED event hasn't been returned yet.
  1034.             //
  1035.             err = OTAckSends(epi->erf);
  1036.             if (err != kOTNoError)
  1037.             {
  1038.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTAckSends error %d", err);
  1039.                 return;
  1040.             }
  1041.             
  1042.             //
  1043.             //    Option Management
  1044.             //
  1045.             //    Turn on ip_reuseaddr so we don't have port conflicts in general.
  1046.             //    We use local stack structures here since the memory for the 
  1047.             //    option request structure is free upon return.   If we were to request
  1048.             //    the option return value, we would have to use static memory for it.
  1049.             //
  1050.             optReq.flags            = T_NEGOTIATE;
  1051.             optReq.opt.len            = kOTFourByteOptionSize;
  1052.             optReq.opt.buf            = (unsigned char *) &opt;
  1053.             
  1054.             opt.len                    = sizeof(TOption);
  1055.             opt.level                = INET_IP;
  1056.             opt.name                = IP_REUSEADDR;
  1057.             opt.status                = 0;
  1058.             opt.value[0]            = 1;
  1059.             
  1060.             err = OTOptionManagement(epi->erf, &optReq, NULL);
  1061.             if (err != kOTNoError)
  1062.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTOptionManagement err %d", err);
  1063.             
  1064.             //
  1065.             //    Code path resumes at T_OPTMGMTCOMPLETE
  1066.             //
  1067.             return;
  1068.         }
  1069.         
  1070.         //
  1071.         //    T_OPTMGMTCOMPLETE:
  1072.         //
  1073.         //    An OTOptionManagement() call has completed.  These are used on all
  1074.         //    endpoints to set IP_REUSEADDR.   It is also used for all endpoints
  1075.         //    other than the listener to set TCP_KEEPALIVE which helps recover
  1076.         //    server resources if the other side crashes or is unreachable.
  1077.         //
  1078.         case T_OPTMGMTCOMPLETE:
  1079.         {
  1080.             TBind                bindReq;
  1081.             InetAddress            inAddr;
  1082.             TOptMgmt             optReq;
  1083.             TKeepAliveOpt        opt;
  1084.             
  1085.             if (result != kOTNoError)
  1086.             {
  1087.                 DBAlert1("Notifier: T_OPTMGMTCOMPLETE result %d", result);
  1088.                 return;
  1089.             }
  1090.             
  1091.             if (epi != gListener)
  1092.             {
  1093.                 if ( OTAtomicSetBit(&epi->stateFlags, kIPReuseAddrBit) == 0 )
  1094.                 {
  1095.                     //
  1096.                     //    Turn on TCP_KEEPALIVE so we can recover from connections which have
  1097.                     //    gone away which we don't know about.   The keepalive value is set
  1098.                     //    very low here, probably too low for a real server.
  1099.                     //
  1100.                     optReq.flags            = T_NEGOTIATE;
  1101.                     optReq.opt.len            = sizeof(TKeepAliveOpt);
  1102.                     optReq.opt.buf            = (unsigned char *) &opt;
  1103.                     
  1104.                     opt.len                    = sizeof(TKeepAliveOpt);
  1105.                     opt.level                = INET_TCP;
  1106.                     opt.name                = TCP_KEEPALIVE;
  1107.                     opt.status                = 0;
  1108.                     opt.tcpKeepAliveOn        = 1;
  1109.                     opt.tcpKeepAliveTimer    = kTCPKeepAliveInSecs;    
  1110.                     
  1111.                     err = OTOptionManagement(epi->erf, &optReq, NULL);
  1112.                     if (err != kOTNoError)
  1113.                     {
  1114.                         DBAlert1("Notifier: T_OPTMGMTCOMPLETE - OTOptionManagement err %d", err);
  1115.                         return;
  1116.                     }
  1117.                 }
  1118.                 else
  1119.                 {
  1120.                     //
  1121.                     //    The endpoint now has both IP_REUSEADDR and TCP_KEEPALIVE set.
  1122.                     //    It is ready to go on the free list to accept an inbound connection.
  1123.                     //
  1124.                     OTLIFOEnqueue(gIdleEPs, &epi->link);
  1125.                     OTAtomicAdd32(1, &gCntrIdleEPs);
  1126.                     if (gListenPending)
  1127.                         EnterListenAccept();
  1128.                 }
  1129.                 return;
  1130.             }
  1131.             
  1132.             //
  1133.             //    Must be listener endpoint, do the bind.  Again, we use stack memory for 
  1134.             //    the bind request structure and NULL for the bind return structure.
  1135.             //
  1136.             inAddr.fAddressType     = AF_INET;
  1137.             inAddr.fPort            = gListenerPort;
  1138.             inAddr.fHost            = 0;                // allow inbound connections from any interface
  1139.             
  1140.             bindReq.addr.len         = sizeof(InetAddress);
  1141.             bindReq.addr.buf         = (unsigned char*) &inAddr;
  1142.             bindReq.qlen             = gListenerQueueDepth;
  1143.             
  1144.             err = OTBind(epi->erf, &bindReq, NULL);
  1145.             if (err != kOTNoError)
  1146.                 DBAlert1("Notifier: T_OPTMGMTCOMPLETE - OTBind error %d", err);
  1147.                 
  1148.             return;            // now wait for a T_LISTEN notification
  1149.         }
  1150.         
  1151.         //
  1152.         //    T_MEMORYRELEASED:
  1153.         //
  1154.         //    This event occurs when OpenTransport is done with the buffer passed in via
  1155.         //    an OTSnd() call with AckSends turned on.   The memory is free and we can reuse it.
  1156.         //    
  1157.         //    IMPORTANT NOTE:  This event is reentrant.   That is, this event will interrupt
  1158.         //    our notifier in progress, even interrupting a T_MEMORYRELEASED in progress, so
  1159.         //    it must be coded more carefully than most other events.
  1160.         //
  1161.         case T_MEMORYRELEASED:
  1162.         {
  1163.             OTAtomicAdd32(-1, &epi->outstandingSends);
  1164.             return;
  1165.         }
  1166.         
  1167.         //
  1168.         //    T_ORDREL:
  1169.         //
  1170.         //    This event occurs when an orderly release has been received on the stream.
  1171.         //
  1172.         case T_ORDREL:
  1173.         {
  1174.             err = OTRcvOrderlyDisconnect(epi->erf);
  1175.             if (err != kOTNoError)
  1176.             {
  1177.                 //
  1178.                 //    It is possible for several reasons for the T_ORDREL to have disappeared,
  1179.                 //    or be temporarily hidden, when we attempt the OTRcvOrderlyDisconnect().
  1180.                 //    The best thing to do when this happens is pretend that the event never
  1181.                 //    occured.   We will get another notification of T_ORDREL if the event
  1182.                 //    becomes unhidden later.  Any other form of error is unexpected and 
  1183.                 //    is reported back so we can correct it.
  1184.                 //
  1185.                 if (err == kOTNoReleaseErr)
  1186.                     return;
  1187.  
  1188.                 DBAlert1("Notifier: T_ORDREL - OTRcvOrderlyDisconnect error %d", err);
  1189.                 return;
  1190.             }
  1191.             
  1192.             //
  1193.             //    Sometimes our data sends get stopped with a kOTLookErr
  1194.             //    because of a T_ORDREL from the other side (which doesn't close
  1195.             //    the connection, it just means they are done sending data).
  1196.             //    If so, we still end up in the notifier with the T_ORDREL event,
  1197.             //    but we won't resume sending data unless we explictly check
  1198.             //    here whether or not we need to do so.
  1199.             //
  1200.             if (epi->sendBytes > 0)
  1201.             {
  1202.                 SendData(epi);
  1203.                 return;
  1204.             }
  1205.             
  1206.             //
  1207.             //    Check the endpoint state to see if we are in T_IDLE.  If so,
  1208.             //    the connection is fully broken down and we can unbind and requeue
  1209.             //    the endpoint for reuse.   If not, then wait until we have also done
  1210.             //    an OTSndOrderlyDisconnect, at which time we will also check the state of
  1211.             //    of the endpoint and unbind there if required.
  1212.             //
  1213.             epState = OTGetEndpointState(epi->erf);
  1214.             if (epState == T_IDLE)
  1215.                 CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  1216.                 
  1217.             return;
  1218.         }
  1219.         
  1220.         //
  1221.         //    T_PASSCON:
  1222.         //
  1223.         //    This event happens on the accepting endpoint, not the listening endpoint.
  1224.         //    At this point the connection is fully established and we can begin the
  1225.         //    process of downloading data.  Note that due to a problem in OT it is 
  1226.         //    possible for a T_DATA to beat a T_PASSCON to the notifier.  When this
  1227.         //    happens we note it in the T_DATA case and then start processing the 
  1228.         //    data here.  
  1229.         //
  1230.         case T_PASSCON:
  1231.         {
  1232.             if (result != kOTNoError)
  1233.             {
  1234.                 DBAlert1("Notifier: T_PASSCON result %d", result);
  1235.                 return;
  1236.             }    
  1237.             OTAtomicAdd32(1, &gCntrConnections);
  1238.             OTAtomicAdd32(1, &gCntrTotalConnections);
  1239.             OTAtomicAdd32(1, &gCntrIntervalConnects);
  1240.             if ( OTAtomicSetBit(&epi->stateFlags, kPassconBit) != 0 )
  1241.             {
  1242.                 //
  1243.                 //    A T_DATA previously beat the T_PASSCON to our notifier.
  1244.                 //    Here we help OT out by having deferred data processing until now.
  1245.                 //
  1246.                 ReadData(epi);
  1247.             }
  1248.             return;
  1249.         }
  1250.         
  1251.         //
  1252.         //    T_UNBINDCOMPLETE:
  1253.         //
  1254.         //    This event occurs on completion of an OTUnbind().
  1255.         //    The endpoint is ready for reuse on a new inbound connection.
  1256.         //    Put it back into the queue of idle endpoints.
  1257.         //    Note that the OTLIFO structure has atomic queue and dequeue,
  1258.         //    which can be helpful for synchronization protection.  
  1259.         //
  1260.         case T_UNBINDCOMPLETE:
  1261.         {
  1262.             CheckUnbind(epi, result, kQueueIt);
  1263.             return;
  1264.         }
  1265.         
  1266.         //
  1267.         //    default:
  1268.         //
  1269.         //    There are events which we don't handle, but we don't expect to see
  1270.         //    any of them.   When running in debugging mode while developing a program,
  1271.         //    we exit with an informational alert.   Later, in the production version
  1272.         //    of the program, we ignore the event and try to keep running.
  1273.         //
  1274.         default:
  1275.         {
  1276.             DBAlert1("Notifier: unknown event <%x>", event);
  1277.             return;
  1278.         }
  1279.     }
  1280. }
  1281.  
  1282. //
  1283. //    ReadData:
  1284. //
  1285. //    This routine attempts to read all available data from an endpoint.
  1286. //    Since this routine is only called from inside the notifier in the current
  1287. //    version of OTVirtualServer, it is not necessary to program to handle
  1288. //    getting back a T_DATA notification DURING an OTRcv() call, as would be
  1289. //    the case if we read from outside the notifier.   We must read until we
  1290. //    get a kOTNoDataErr in order to clear the T_DATA event so we will get
  1291. //    another notification of T_DATA in the future.
  1292. //
  1293. //    Currently this application uses no-copy receives to get data.  This obligates
  1294. //    the program to return the buffers to OT asap.  Since this program does nothing
  1295. //    with data other than count it, that's easy.  Future, more complex versions
  1296. //    of this program will do more interesting things with regards to that.
  1297. //
  1298. static void ReadData(EPInfo* epi)
  1299. {
  1300.     OTBuffer*    bp;
  1301.     OTResult    res;
  1302.     OTFlags        flags;
  1303.     OTResult    epState;
  1304.     Boolean        gotRequest = false;
  1305.     
  1306.     while (true)
  1307.     {
  1308.         res = OTRcv(epi->erf, &bp, kOTNetbufDataIsOTBufferStar, &flags);
  1309.         
  1310.         //
  1311.         //    Note, check for 0 because can get a real 0 length recive
  1312.         //    in some protocols (not in TCP), which is different from
  1313.         //    getting back a kOTNoDataErr.
  1314.         //
  1315.         if  (res >= 0 )
  1316.         {
  1317.             OTAtomicAdd32(res, &epi->rcvdBytes);
  1318.             OTAtomicAdd32(res, &gCntrIntervalBytes);
  1319.             OTReleaseBuffer(bp);
  1320.             if (epi->rcvdBytes >= kRequestSize)
  1321.             {
  1322.                 if (OTAtomicSetBit(&epi->stateFlags, kGotRequestBit) == 0)
  1323.                 {
  1324.                     //
  1325.                     //    We have gotten our 128 byte data request, so prepare to respond.
  1326.                     //    By setting the bit, we make sure that we can handle requests
  1327.                     //    which are bigger than expected without going weird.
  1328.                     //
  1329.                     epi->sendPtr    = gDataBuf;
  1330.                     epi->sendBytes    = gReturnDataLength;
  1331.                 }
  1332.             }
  1333.             continue;
  1334.         }
  1335.         
  1336.         if (res == kOTNoDataErr)
  1337.         {
  1338.             //
  1339.             //    Since ReadData is only called from inside the notifier we don't 
  1340.             //    have to worry about having missed a T_DATA during an OTRcv.
  1341.             //
  1342.             break;
  1343.         }
  1344.         
  1345.         if (res == kOTLookErr)
  1346.         {
  1347.             res = OTLook(epi->erf);
  1348.             if (res == T_ORDREL)
  1349.             {
  1350.                 //    
  1351.                 //    If we got the T_ORDREL, we won't get any more inbound data.
  1352.                 //    We return and wait for the notifier to get the T_ORDREL notification.
  1353.                 //    Upon getting it, we will notice we still need to send data and do so.
  1354.                 //    The T_ORDREL has to be cleared before we can send. 
  1355.                 //
  1356.                 return;
  1357.             }
  1358.             if (res == T_GODATA)
  1359.                 continue;
  1360.             
  1361.             DBAlert1("ReadData: OTRcv got OTLookErr 0x%08x", res);
  1362.         }
  1363.         else
  1364.         {
  1365.             epState = OTGetEndpointState(epi->erf);
  1366.             if (res == kOTOutStateErr && epState == T_INREL)
  1367.             {
  1368.                 //
  1369.                 //    Occasionally this problem will happen due to what appears
  1370.                 //    to be an OpenTransport notifier reentrancy problem.   
  1371.                 //    What has occured is that a T_ORDREL event happened and 
  1372.                 //    was processed during ReadData().   This is proven by being
  1373.                 //    in the T_INREL state without having done a call to
  1374.                 //    OTRcvOrderlyDisconnect() here.   It appears to be a benign 
  1375.                 //    situation, so the way to handle it is to understand that no
  1376.                 //    more data is going to arrive and go ahead and being our response
  1377.                 //    to the client.
  1378.                 //
  1379.                 break;
  1380.             }
  1381.             
  1382.             DBAlert2("ReadData: OTRcv error %d state %d", res, epState);
  1383.         }
  1384.         return;
  1385.     }
  1386.     
  1387.     SendData(epi);
  1388. }
  1389.  
  1390. //
  1391. //    Recycle:
  1392. //
  1393. //    This routine shouldn't be necessary, but it is helpful to work around both
  1394. //    problems in OpenTransport and bugs in this program.   Basically, whenever an
  1395. //    unexpected error occurs which shouldn't be fatal to the program, the EPInfo
  1396. //    is queued on the BrokenEP queue.  When recycle is called, once per pass around
  1397. //    the event loop, it will attempt to close the associated endpoint and open
  1398. //    a new one to replace it using the same EPInfo structure.   This process of
  1399. //    closing an errant endpoint and opening a replacement is probably the most
  1400. //    reliable way to make sure that this program and OpenTransport can recover
  1401. //    from unexpected happenings in a clean manner.
  1402. //
  1403. static void Recycle()
  1404. {
  1405.     OTLink*     list = OTLIFOStealList(gBrokenEPs);
  1406.     OTLink*        link;
  1407.     EPInfo*        epi;
  1408.  
  1409.     while ( (link = list) != NULL )
  1410.     {
  1411.         list = link->fNext;
  1412.         epi = OTGetLinkObject(link, EPInfo, link);
  1413.         if (!EPClose(epi))
  1414.         {
  1415.             OTLIFOEnqueue(gBrokenEPs, &epi->link);
  1416.             continue;
  1417.         }
  1418.         OTAtomicClearBit(&epi->stateFlags, kBrokenBit);
  1419.         OTAtomicAdd32(-1, &gCntrBrokenEPs);
  1420.         EPOpen(epi, OTCloneConfiguration(gCfgMaster));
  1421.     }
  1422. }
  1423.  
  1424. //
  1425. //    SendData:
  1426. //
  1427. //    For this first, simple version of the OT Virtual Server, we just send
  1428. //    a predefined number of bytes from a RAM buffer and then start an orderly
  1429. //    release sequence.  The assumption here is that we can send the entire buffer 
  1430. //    in one send.  Obviously future versions of this sample will have to do 
  1431. //    the OTSnd() in a more sophisticated way. 
  1432. //
  1433. static void SendData(EPInfo* epi)
  1434. {
  1435.     OTResult res;
  1436.     
  1437.     if (epi->sendBytes == 0)
  1438.         return;
  1439.         
  1440.     //
  1441.     //    Make sure we record that we are starting a send so we don't try to close
  1442.     //    the endpoint before a T_MEMORYRELEASED event is returned.
  1443.     //
  1444.     OTAtomicAdd32(1, &epi->outstandingSends);
  1445.     
  1446.     //
  1447.     //    In OT 1.1.2 and previous versions, there is a bug with AckSends
  1448.     //    which occurs when the same buffer is sent more than once.   In an attempt
  1449.     //    to go fast and not allocate memory, TCP may write an IP and TCP header
  1450.     //    into the data buffer which is sent.   If the buffer is sent more than once
  1451.     //    without being refreshed, the data may be corrupted.   To work around this,
  1452.     //    send the data via an OTData structure, using the gather-write mechanism.
  1453.     //    The problem does not occur in this code path, and this will not hinder performance.
  1454.     //    The problem will be fixed in the next Open Transport release following 1.1.2.
  1455.     //
  1456.     if (gOTVersion < kOTVersion113)
  1457.     {
  1458.         struct OTData data;
  1459.         
  1460.         data.fNext = NULL;
  1461.         data.fData = epi->sendPtr;
  1462.         data.fLen  = epi->sendBytes;
  1463.         res = OTSnd(epi->erf, &data, kNetbufDataIsOTData, 0);
  1464.     }
  1465.     else
  1466.     {
  1467.         res = OTSnd(epi->erf, epi->sendPtr, epi->sendBytes, 0);
  1468.     }
  1469.     if (res == gReturnDataLength)
  1470.     {
  1471.         //
  1472.         //    The entire buffer was accepted and we can begin the orderly release process.
  1473.         //
  1474.         OTAtomicAdd32(res, &gCntrTotalBytesSent);
  1475.         OTAtomicAdd32(res, &gCntrIntervalBytes);
  1476.         epi->sendPtr     = NULL;
  1477.         epi->sendBytes     = 0;
  1478.         
  1479.         if (gOTVersion < kOTVersion113)
  1480.         {
  1481.             //
  1482.             //    OT 1.1.2 and earlier versions have a bug in OT/TCP where 
  1483.             //    OT/TCP can lose an inbound orderly release if the orderly releases
  1484.             //    cross AND there is no time for the STREAMS service routines to fire.
  1485.             //    The workaround is to force the system back to system task time,
  1486.             //    and the event loop, before doing the orderly release.  This costs
  1487.             //    about 18% in connections/second performance in my testing, but
  1488.             //    the workaround is 100% reliable.   Here we set a stateFlag bit
  1489.             //    just in case the connection is disconnected while it is waiting
  1490.             //    for the orderly release to occur.
  1491.             //
  1492.             OTAtomicSetBit(&epi->stateFlags, kWaitingBit);
  1493.             OTLIFOEnqueue(gWaitingEPs, &epi->link);
  1494.         }
  1495.         else
  1496.             DoSndOrderlyDisconnect(epi);
  1497.             
  1498.         return;
  1499.     }
  1500.         
  1501.     if (res > 0)
  1502.     {
  1503.         //
  1504.         //    Implied kOTFlowErr since not all data was accepted.
  1505.         //    Currently SendData is only invoked from inside the notifier.
  1506.         //    If it was called from outside the notifier, it would need race
  1507.         //    protection against the T_GODATA happening before the OTSnd returned.
  1508.         //
  1509.         OTAtomicAdd32(res, &gCntrTotalBytesSent);
  1510.         OTAtomicAdd32(res, &gCntrIntervalBytes);
  1511.         epi->sendPtr     += res;
  1512.         epi->sendBytes     -= res;
  1513.     }
  1514.     
  1515.     else        // res =< 0
  1516.     {
  1517.         OTAtomicAdd32(-1, &epi->outstandingSends);
  1518.         if (res == kOTFlowErr)
  1519.             return;
  1520.         if (res == kOTLookErr)
  1521.         {
  1522.             res = OTLook(epi->erf);
  1523.             if (res == T_ORDREL)
  1524.             {
  1525.                 //
  1526.                 //    Wait to get the T_ORDREL at the notifier and handle it there.
  1527.                 //    Then we will resume sending.
  1528.                 //
  1529.                 return;
  1530.             }
  1531.             else
  1532.             {    
  1533.                 DBAlert1("SendData: OTSnd LOOK error %d", res);
  1534.             }
  1535.         }
  1536.         else
  1537.         {
  1538.             DBAlert1("SendData OTSnd error %d", res);
  1539.         }
  1540.     }
  1541. }
  1542.  
  1543. //
  1544. //    StartServer:
  1545. //
  1546. //    This routine gets memory for EPInfo structures.  It gets one for the listener 
  1547. //    endpoint and one for each of the acceptor endpoints.
  1548. //
  1549. static void StartServer()
  1550. {
  1551.     int        i;
  1552.     EPInfo* epi;
  1553.     size_t    bytes;
  1554.     
  1555.     gCntrEndpts                = 0;
  1556.     gCntrIdleEPs            = 0;
  1557.     gCntrTotalBrokenEPs        = 0;
  1558.     gCntrBrokenEPs            = 0;
  1559.     gCntrTotalBrokenEPs        = 0;
  1560.     gCntrTotalConnections    = 0;
  1561.     gCntrTotalBytesSent        = 0;
  1562.     gIdleEPs->fHead            = NULL;
  1563.     gBrokenEPs->fHead         = NULL;
  1564.     gWaitingEPs->fHead         = NULL;
  1565.     gServerState            = kServerRunning;
  1566.     
  1567.     //
  1568.     //    Save the current setting of max connections so we don't lose
  1569.     //    track of how much memory we will get if someone changes the
  1570.     //    dialog while the server is running.
  1571.     //
  1572.     gMaxConnectionsAllowed    = gMaxConnections;        
  1573.     
  1574.     //
  1575.     //    Get a block of memory to hold all the EPInfo structures.
  1576.     //    We use the first one for the listener.
  1577.     //    The rest are treated as an array of acceptors.
  1578.     //
  1579.     bytes = (gMaxConnectionsAllowed + 1) * sizeof(EPInfo);
  1580.     epi = (EPInfo*) NewPtr(bytes);
  1581.     if (epi == NULL)
  1582.     {
  1583.         DoAlert("Cannot get enough memory to allocate endpoints, exiting");
  1584.         return;
  1585.     }
  1586.     OTMemzero(epi, bytes);
  1587.     gListener    = epi++;
  1588.     gAcceptors    = epi;
  1589.     
  1590.     //
  1591.     //    Open listener, using the tilisten module to make 
  1592.     //    listen/accept/disconnect processing much simpler.
  1593.     //
  1594.     if (!EPOpen(gListener, OTCreateConfiguration("tilisten, tcp")))
  1595.         return;
  1596.             
  1597.     //
  1598.     //    Open endpoints to accept inbound connections.
  1599.     //    Note that any configuration passed in to OTOpenEndpoint is destroyed,
  1600.     //    so we create a master configuration, clone it once for each connection,
  1601.     //    which saves a lot of OT processing, and then destroy the master 
  1602.     //    configuration at the end.
  1603.     //
  1604.     gCfgMaster = OTCreateConfiguration("tcp");
  1605.     if (gCfgMaster == NULL)
  1606.     {
  1607.         DBAlert("StartServer: OTCreateConfiguration returned NULL");
  1608.         return;
  1609.     }
  1610.     for (epi = gAcceptors, i = 0; i < gMaxConnectionsAllowed; epi++, i++)
  1611.     {
  1612.         if (!EPOpen(epi, OTCloneConfiguration(gCfgMaster)))
  1613.             break;
  1614.     }
  1615. }
  1616.  
  1617. //
  1618. //    StopServer:
  1619. //
  1620. //    This is where the server is shut down, either because the user clicked
  1621. //    the stop button, or because the program is exiting (error or quit).
  1622. //    The two tricky parts are (1) we can't quit while there are outstanding
  1623. //    OTAsyncOpenEndpoint calls (which can't be cancelled, by the way), and
  1624. //    (2) we can't close endpoints until that have received all expected
  1625. //    T_MEMORYRELEASED events.
  1626. //
  1627. static void StopServer()
  1628. {
  1629.     int            i;
  1630.     EPInfo        *epi;
  1631.     Boolean        allClosed = true;
  1632.     
  1633.     gServerState = kServerShuttingDown;
  1634.     
  1635.     //
  1636.     //    Since the LIFOs shouldn't be used any longer, we clear them here.
  1637.     //
  1638.     (void) OTLIFOStealList(gBrokenEPs);
  1639.     (void) OTLIFOStealList(gIdleEPs);
  1640.     (void) OTLIFOStealList(gWaitingEPs);
  1641.     
  1642.     //
  1643.     //    Attempt to close all endpoints.
  1644.     //    EPClose doesn't mind being called again with epi->erf == NULL.
  1645.     //
  1646.     for (epi = gListener, i = 0; i < (gMaxConnectionsAllowed + 1); epi++, i++)
  1647.     {
  1648.         if (!EPClose(epi))
  1649.             allClosed = false;
  1650.     }
  1651.     
  1652.     //
  1653.     //    If we successfully deleted all of the endpoints, we can release
  1654.     //    the memory and head home for Christmas now...
  1655.     //
  1656.     if (allClosed)
  1657.     {
  1658.         DisposePtr((char*)gListener);
  1659.         OTDestroyConfiguration(gCfgMaster);
  1660.         gListener             = NULL;
  1661.         gAcceptors             = NULL;
  1662.         gCntrIdleEPs        = 0;
  1663.         gCntrBrokenEPs        = 0;
  1664.         gCntrConnections    = 0;
  1665.         gServerState         = kServerStopped;
  1666.     }
  1667. }
  1668.  
  1669. //
  1670. //    TimerInit
  1671. //
  1672. //    Start up a regular timer to do housekeeping.   Strictly speaking,
  1673. //    this isn't necessary, but having a regular heartbeat allows us to
  1674. //    detect if we are so busy with network notifier processing that the
  1675. //    program's event loop isn't ever firing.   We want to know this so
  1676. //    we can at least allow the user to quit the program if they want to.
  1677. //
  1678. static void TimerInit()
  1679. {
  1680.     gTimerTask = OTCreateTimerTask(&TimerRun, 0);
  1681.     if (gTimerTask == 0)
  1682.     {
  1683.         DBAlert("TimerInit: OTCreateTimerTask returned 0");
  1684.         return;
  1685.     }
  1686.     OTScheduleTimerTask(gTimerTask, kTimerInterval);
  1687. }
  1688.  
  1689. //
  1690. //    TimerDestroy
  1691. //
  1692. static void TimerDestroy()
  1693. {
  1694.     if (gTimerTask != 0)
  1695.     {
  1696.         OTCancelTimerTask(gTimerTask);
  1697.         OTDestroyTimerTask(gTimerTask);
  1698.         gTimerTask = 0;
  1699.     }
  1700. }
  1701.  
  1702. //
  1703. //    TimerRun
  1704. //
  1705. //    Fires every N seconds, no matter how busy the system is.
  1706. //    We use this to detect if the program's main event loop is getting no time,
  1707. //    in which case we can slow the server down by doing a throttle-back until
  1708. //    the event loop can run at least once.  It also is a convenient statistics 
  1709. //    gathering point.  
  1710. //
  1711. static pascal void TimerRun(void*)
  1712. {
  1713.     gConnectsPerSecond = (gCntrIntervalConnects / kTimerIntervalInSeconds);
  1714.     gKBytesPerSecond = (gCntrIntervalBytes / (kTimerIntervalInSeconds * 1024));
  1715.     gEventsPerSecond = (gCntrIntervalEventLoop / kTimerIntervalInSeconds);
  1716.     if (gCntrIntervalEventLoop == 0)
  1717.         gWaitForEventLoop = true;
  1718.     
  1719.     if (gEventsPerSecond > gEventsPerSecondMax)
  1720.         gEventsPerSecondMax = gEventsPerSecond;
  1721.         
  1722.     if (gAllowNewMax == 0)
  1723.     {
  1724.         //
  1725.         //    Avoid bytes/second data skewing from early buffering by not allowing 
  1726.         //    the first non-zero measurement to be saved as a max.  We could use an 
  1727.         //    exponential weighted average instead, but since our timer doesn't fire 
  1728.         //    very often, the stats take too long to become valid that way.
  1729.         //
  1730.         if (gConnectsPerSecond > gConnectsPerSecondMax)
  1731.             gConnectsPerSecondMax = gConnectsPerSecond;
  1732.         if (gKBytesPerSecond > gKBytesPerSecondMax)
  1733.             gKBytesPerSecondMax = gKBytesPerSecond;
  1734.     }
  1735.     
  1736.     if (gConnectsPerSecond > 0)
  1737.     {
  1738.         if (gAllowNewMax > 0)
  1739.             gAllowNewMax--;
  1740.     }
  1741.     else
  1742.         gAllowNewMax = kTimerHitsBeforeAcceptMax;
  1743.  
  1744.     gCntrIntervalConnects    = 0;
  1745.     gCntrIntervalBytes        = 0;
  1746.     gCntrIntervalEventLoop    = 0;
  1747.     gDoWindowUpdate            = true;
  1748.     gCntrConnections        = gCntrEndpts - gCntrIdleEPs - gCntrBrokenEPs;
  1749.     
  1750.     OTScheduleTimerTask(gTimerTask, kTimerInterval);
  1751. }
  1752.  
  1753.  
  1754. //////////////////////////////////////////////////////////////////////////////////////
  1755. //
  1756. //    Macintosh Program Wrapper
  1757. //
  1758. //    The code from here down deals with the Macintosh environment, events,
  1759. //    menus, command keys, etc.   Networking code is in the section above.
  1760. //    Since this code is fairly basic, and since this isn't really intended
  1761. //    to be a "sample Macintosh application" (just a sample OpenTransport application)
  1762. //    this section isn't heavily commented.   There are much better Macintosh
  1763. //    application samples for handling mouse, keyboard, event loops, etc.
  1764. //
  1765. //////////////////////////////////////////////////////////////////////////////////////
  1766.  
  1767. static void AboutBox()
  1768. {
  1769.     Alert(kAboutBoxResID, NULL);
  1770. }
  1771.  
  1772. static Boolean EventDialog(EventRecord* event)
  1773. {
  1774.     DialogPtr     dp;
  1775.     short        item;
  1776.     short        itemType;
  1777.     Handle        itemHandle;
  1778.     Rect        itemRect;
  1779.     
  1780.     if (event->modifiers & cmdKey)
  1781.     {
  1782.         EventKeyDown(event);        // this allows menu commands while dialog is active window
  1783.         return false;                // note if I add cut/paste I will have to rework this.
  1784.     }
  1785.     if ((DialogSelect(event, &dp, &item)) && (dp == gDialogPtr))
  1786.     {
  1787.         GetDialogItem(gDialogPtr, item, &itemType, &itemHandle, &itemRect);
  1788.         switch (item)
  1789.         {
  1790.             case kListenerPortDItem:
  1791.                 GetDialogItemText(itemHandle, gListenerPortStr);
  1792.                 return true;
  1793.             
  1794.             case kListenerQueueDepthDItem:
  1795.                 GetDialogItemText(itemHandle, gListenerQueueDepthStr);
  1796.                 return true;
  1797.             
  1798.             case kMaxConnectionsDItem:
  1799.                 GetDialogItemText(itemHandle, gMaxConnectionsStr);
  1800.                 return true;
  1801.             
  1802.             case kReturnDataLengthDItem:
  1803.                 GetDialogItemText(itemHandle, gReturnDataLengthStr);
  1804.                 return true;
  1805.                 
  1806.             case kStartStopDItem:
  1807.                 GetDialogItem(gDialogPtr, kStartStopDItem, &itemType, &itemHandle, &itemRect);
  1808.                 if (gServerRunning)
  1809.                 {
  1810.                     StopServer();                
  1811.                     SetControlTitle((ControlHandle)itemHandle, gStartStr);
  1812.                     gServerRunning = false;
  1813.                 }
  1814.                 else
  1815.                 {
  1816.                     TCPPrefsReset();
  1817.                     StartServer();
  1818.                     SetControlTitle((ControlHandle)itemHandle, gStopStr);
  1819.                     gServerRunning = true;
  1820.                 }
  1821.                 DrawDialog(gDialogPtr);
  1822.                 return true;
  1823.         }
  1824.     }
  1825.     return false;
  1826. }
  1827.  
  1828. static void TCPPrefsReset()
  1829. {
  1830.     StringToNum(gListenerPortStr, &gListenerPort);
  1831.     StringToNum(gListenerQueueDepthStr, &gListenerQueueDepth);
  1832.     StringToNum(gMaxConnectionsStr, &gMaxConnections);
  1833.     StringToNum(gReturnDataLengthStr, &gReturnDataLength);
  1834.     if (gReturnDataLength > kDataBufSize)
  1835.         gReturnDataLength = kDataBufSize;
  1836. }
  1837.  
  1838. static void TCPPrefsDialog()
  1839. {
  1840.     short    itemType;
  1841.     Handle    itemHandle;
  1842.     Rect    itemRect;
  1843.     
  1844.     gDialogPtr = GetNewDialog(kTCPPrefsDlogResID, NULL, kInFront);
  1845.     SetWTitle(gDialogPtr, "\pTCP Preferences");
  1846.     GetDialogItem(gDialogPtr, kListenerPortDItem, &itemType, &itemHandle, &itemRect);
  1847.     SetDialogItemText(itemHandle, gListenerPortStr);
  1848.     GetDialogItem(gDialogPtr, kListenerQueueDepthDItem, &itemType, &itemHandle, &itemRect);
  1849.     SetDialogItemText(itemHandle, gListenerQueueDepthStr);
  1850.     GetDialogItem(gDialogPtr, kMaxConnectionsDItem, &itemType, &itemHandle, &itemRect);
  1851.     SetDialogItemText(itemHandle, gMaxConnectionsStr);
  1852.     GetDialogItem(gDialogPtr, kReturnDataLengthDItem, &itemType, &itemHandle, &itemRect);
  1853.     SetDialogItemText(itemHandle, gReturnDataLengthStr);
  1854.     GetDialogItem(gDialogPtr, kStartStopDItem, &itemType, &itemHandle, &itemRect);
  1855.     if (gServerRunning)
  1856.         SetControlTitle((ControlHandle)itemHandle, gStopStr);
  1857.     else
  1858.         SetControlTitle((ControlHandle)itemHandle, gStartStr);
  1859.     DrawDialog(gDialogPtr);
  1860. }
  1861.  
  1862. static void DialogClose()
  1863. {
  1864.     DisposeDialog(gDialogPtr);
  1865.     gDialogPtr = NULL;
  1866.     TCPPrefsReset();
  1867. }
  1868.  
  1869. static void MenuDispatch(long menu)
  1870. {
  1871.     short menuID;
  1872.     short cmdID;
  1873.     
  1874.     menuID = HiWord(menu);
  1875.     cmdID  = LoWord(menu);
  1876.     switch(menuID)
  1877.     {
  1878.         case kAppleMenuResID:
  1879.         {
  1880.             switch (cmdID)
  1881.             {
  1882.                 case kAppleMenuAbout:
  1883.                     AboutBox();
  1884.                     break;
  1885.                     
  1886.                 default:
  1887.                     break;
  1888.             }
  1889.             break;
  1890.         }
  1891.             
  1892.         case kFileMenuResID:
  1893.         {
  1894.             switch (cmdID)
  1895.             {
  1896.                 case kFileMenuQuit:
  1897.                     gProgramState = kProgramDone;
  1898.                     break;
  1899.                     
  1900.                 case kFileMenuOpen:
  1901.                     WindowOpen();
  1902.                     break;
  1903.                     
  1904.                 case kFileMenuClose:
  1905.                     WindowClose();
  1906.                     break;
  1907.                     
  1908.                 default:
  1909.                     break;
  1910.             }
  1911.             break;
  1912.         }
  1913.         
  1914.         case kEditMenuResID:
  1915.             break;
  1916.         
  1917.         case kServerMenuResID:
  1918.         {
  1919.             switch (cmdID)
  1920.             {
  1921.                 case kServerMenuTCPPrefs:
  1922.                     TCPPrefsDialog();
  1923.                     break;
  1924.                     
  1925.                 default:
  1926.                     break;
  1927.             }
  1928.             break;
  1929.         }
  1930.     }
  1931.         
  1932. }
  1933.  
  1934. static void EventDrag(WindowPtr wp, Point loc)
  1935. {
  1936.     Rect dragBounds;
  1937.     
  1938.     dragBounds = qd.screenBits.bounds;
  1939.     DragWindow(wp, loc, &dragBounds);
  1940. }
  1941.  
  1942. static void EventGoAway(WindowPtr wp, Point loc)
  1943. {
  1944.     if (TrackGoAway(wp, loc))
  1945.     {
  1946.         if (wp == gWindowPtr)
  1947.             WindowClose();
  1948.         else if (wp == gDialogPtr)
  1949.             DialogClose();
  1950.     }
  1951. }
  1952.     
  1953. static void EventMouseDown(EventRecord* event)
  1954. {
  1955.     short        part;
  1956.     WindowPtr    wp;
  1957.     long         menu;
  1958.     
  1959.     part = FindWindow(event->where, &wp);
  1960.     switch (part)
  1961.     {
  1962.         case inMenuBar:
  1963.             menu = MenuSelect(event->where);
  1964.             HiliteMenu(0);
  1965.             MenuDispatch(menu);
  1966.             break;
  1967.             
  1968.         case inDrag:
  1969.             EventDrag(wp, event->where);
  1970.             break;
  1971.         
  1972.         case inGoAway:
  1973.             EventGoAway(wp, event->where);
  1974.             break;
  1975.         
  1976.         case inContent:        
  1977.             SelectWindow(wp);
  1978.             break;
  1979.             
  1980.         case inGrow:        // no grow box
  1981.         case inZoomIn:        // no zoom box
  1982.         case inZoomOut:        // no zoom box
  1983.         case inSysWindow:
  1984.         case inDesk:
  1985.         default:
  1986.             break;
  1987.     }
  1988. }
  1989.  
  1990. static void EventKeyDown(EventRecord* event)
  1991. {
  1992.     char        c;
  1993.     long        menu;
  1994.     
  1995.     c = event->message & charCodeMask;
  1996.     if (event->modifiers & cmdKey)
  1997.     {
  1998.         // cmd key
  1999.         menu = MenuKey(c);
  2000.         HiliteMenu(0);
  2001.         if (menu != 0)
  2002.             MenuDispatch(menu);
  2003.     }
  2004.     else
  2005.     {
  2006.         // normal keystroke
  2007.     }
  2008. }
  2009.  
  2010. static void EventLoop()
  2011. {
  2012.     EventRecord event;
  2013.     
  2014.     while ((gProgramState == kProgramRunning) || (gServerState != kServerStopped))
  2015.     {
  2016.         OTAtomicAdd32(1, &gCntrIntervalEventLoop);
  2017.         if (WaitNextEvent(everyEvent, &event, gSleepTicks, 0)) 
  2018.         {
  2019.             if ((gDialogPtr != NULL) && (IsDialogEvent(&event)))
  2020.             {
  2021.                 if (EventDialog(&event))
  2022.                     continue;
  2023.             }
  2024.             switch (event.what)
  2025.             {
  2026.                 case keyDown:
  2027.                     EventKeyDown(&event);
  2028.                     break;
  2029.                     
  2030.                 case mouseDown:
  2031.                     EventMouseDown(&event);
  2032.                     break;
  2033.                     
  2034.                 case updateEvt:
  2035.                     // redraw window now
  2036.                     break;
  2037.                 
  2038.                 case activateEvt:
  2039.                     // activate or deactivate window controls
  2040.                     break;
  2041.                 
  2042.                 case mouseUp:
  2043.                 case keyUp:
  2044.                 case autoKey:
  2045.                 case diskEvt:
  2046.                 case app4Evt:
  2047.                 default:
  2048.                     break;
  2049.             }
  2050.         }
  2051.         
  2052.         if ((gProgramState == kProgramRunning) && (gServerState == kServerRunning))
  2053.         {
  2054.             NetEventLoop();
  2055.         }
  2056.         else if (((gProgramState == kProgramRunning) && (gServerState == kServerShuttingDown)) ||
  2057.                  ((gProgramState != kProgramRunning) && (gServerState != kServerStopped)))
  2058.         {
  2059.             StopServer();
  2060.         }
  2061.         WindowUpdate();
  2062.     }
  2063. }
  2064.  
  2065. static void WindowClose()
  2066. {
  2067.     if (gWindowPtr == NULL)
  2068.         return;
  2069.     DisposeWindow(gWindowPtr);
  2070.     gWindowPtr = NULL;
  2071. }
  2072.  
  2073. static void WindowOpen()
  2074. {
  2075.     if (gWindowPtr != NULL)
  2076.         return;
  2077.     gWindowPtr = GetNewWindow(kWindowResID, NULL, kInFront);
  2078.     SetWTitle(gWindowPtr, "\pOTVirtualServer");
  2079. }
  2080.  
  2081. static void WindowUpdate()
  2082. {
  2083.     char gStrBuf[128];
  2084.     int len;
  2085.     
  2086.     if (gWindowPtr == NULL)
  2087.         return;
  2088.         
  2089.     if (gDoWindowUpdate == false)
  2090.         return;
  2091.     gDoWindowUpdate = false;
  2092.         
  2093.     gCntrConnections = gCntrEndpts - gCntrIdleEPs - gCntrBrokenEPs;
  2094.     
  2095.     SetPort(gWindowPtr);
  2096.     EraseRgn(gWindowPtr->visRgn);
  2097.     
  2098.     MoveTo(20, 20);
  2099.     sprintf(gStrBuf, "EPs: total %d idle %d", gCntrEndpts, gCntrIdleEPs);
  2100.     len = strlen(gStrBuf) ;
  2101.     DrawText(gStrBuf, 0, len);
  2102.     
  2103.     MoveTo(20, 40);
  2104.     sprintf(gStrBuf, "Connects: current %d total %d", gCntrConnections, gCntrTotalConnections);
  2105.     len = strlen(gStrBuf) ;
  2106.     DrawText(gStrBuf, 0, len);
  2107.  
  2108.     MoveTo(20, 60);
  2109.     sprintf(gStrBuf, "KBytes sent %d", (gCntrTotalBytesSent / 1024));
  2110.     len = strlen(gStrBuf) ;
  2111.     DrawText(gStrBuf, 0, len);
  2112.         
  2113.     MoveTo(20, 80);
  2114.     sprintf(gStrBuf, "Conn/sec: current %d max %d", gConnectsPerSecond, gConnectsPerSecondMax);
  2115.     len = strlen(gStrBuf) ;
  2116.     DrawText(gStrBuf, 0, len);
  2117.     
  2118.     MoveTo(20, 100);
  2119.     sprintf(gStrBuf, "KBy/sec: current %d max %d", gKBytesPerSecond, gKBytesPerSecondMax);
  2120.     len = strlen(gStrBuf) ;
  2121.     DrawText(gStrBuf, 0, len);
  2122.     
  2123.     MoveTo(20, 120);
  2124.     sprintf(gStrBuf, "Events/sec: %d/%d", gEventsPerSecond, gEventsPerSecondMax);
  2125.     len = strlen(gStrBuf) ;
  2126.     DrawText(gStrBuf, 0, len);
  2127.     
  2128.     MoveTo(20, 140);
  2129.     sprintf(gStrBuf, "Running at %d%% of capacity.", 
  2130.             (100 - ((100 * gEventsPerSecond)/gEventsPerSecondMax)));
  2131.     len = strlen(gStrBuf) ;
  2132.     DrawText(gStrBuf, 0, len);
  2133.     
  2134.     MoveTo(20, 160);
  2135.     sprintf(gStrBuf, "Broken EPs: %d total: %d.", gCntrBrokenEPs, gCntrTotalBrokenEPs);
  2136.     len = strlen(gStrBuf) ;
  2137.     DrawText(gStrBuf, 0, len);
  2138.  
  2139.     MoveTo(20, 180);
  2140.     sprintf(gStrBuf, "OTVersion 0x%08x", gOTVersion);
  2141.     len = strlen(gStrBuf) ;
  2142.     DrawText(gStrBuf, 0, len);
  2143.  
  2144. }
  2145.  
  2146. static void SetupMenus()
  2147. {
  2148.     MenuHandle mh;
  2149.     mh = GetMenu(kAppleMenuResID);
  2150.     AppendResMenu( mh, 'DRVR' );            /* Add DA list */
  2151.     InsertMenu(mh, 0);
  2152.     mh = GetMenu(kFileMenuResID);
  2153.     InsertMenu(mh, 0);
  2154.     mh = GetMenu(kEditMenuResID);
  2155.     InsertMenu(mh, 0);
  2156.     mh = GetMenu(kServerMenuResID);
  2157.     InsertMenu(mh, 0);
  2158.     DrawMenuBar();
  2159. }
  2160.  
  2161. static void MyC2PStr(char* cstr, Str255 pstr)
  2162. {
  2163.     //
  2164.     //    Converts a C string to a Pascal string.
  2165.     //    Truncates the string if longer than 254 bytes.
  2166.     //
  2167.     int i, j;
  2168.     
  2169.     i = strlen(cstr);
  2170.     if (i > 254)
  2171.         i = 254;
  2172.     pstr[0] = i;
  2173.     for (j = 1; j <= i; j++)
  2174.         pstr[j] = cstr[j-1];
  2175. }
  2176.  
  2177. static void MyP2CStr(Str255 pstr, char* cstr)
  2178. {
  2179.     int i;
  2180.     
  2181.     for (i = 0; i < pstr[0]; i++)
  2182.         cstr[i] = pstr[i+1];
  2183.     cstr[i] = 0;
  2184. }
  2185.  
  2186. static void AlertExit(char* err)
  2187. {
  2188.     Str255 pErr;
  2189.     
  2190.     MyC2PStr(err, pErr);
  2191.     ParamText(pErr, NULL, NULL, NULL);
  2192.     Alert(kAlertExitResID, NULL);
  2193.     ExitToShell();    
  2194. }
  2195.  
  2196. static void MacInitROM()
  2197. {
  2198.     MaxApplZone();
  2199.     MoreMasters();
  2200.     InitGraf(&qd.thePort);
  2201.     InitCursor();
  2202.     InitFonts();
  2203.     InitWindows();
  2204.     InitMenus();
  2205.     TEInit();
  2206.     InitDialogs(NULL);
  2207.     FlushEvents(everyEvent, 0);
  2208. }
  2209.  
  2210. static void MacInit()
  2211. {
  2212.     MacInitROM();
  2213.     WindowOpen();
  2214.     SetupMenus();
  2215. }
  2216.  
  2217. static void MiscInit()
  2218. {
  2219.     //    Initialize the temporary data buffer so it isn't all zeros.
  2220.  
  2221.     int i;
  2222.     unsigned char x = 0;
  2223.     
  2224.     for (i = 0; i < kDataBufSize; i++)
  2225.         gDataBuf[i] = x++;
  2226. }
  2227.  
  2228. void main()
  2229. {
  2230.     MacInit();
  2231.     NetInit();
  2232.     MiscInit();
  2233.     EventLoop();
  2234.     NetShutdown();
  2235.     if (gProgramState == kProgramError)
  2236.         AlertExit(gProgramErr);
  2237. }
  2238.